package cn.zhuwj.autodiscovery.core;

import cn.zhuwj.autodiscovery.common.Constant;
import cn.zhuwj.autodiscovery.config.StartConfig;
import cn.zhuwj.autodiscovery.utils.NetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zhuwj.cn
 */
public class TcpPortThread extends Thread {

    private final Logger logger = LoggerFactory.getLogger(TcpPortThread.class);

    static {
        System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG");
    }

    private int offsetPort = 0;
    private CountDownLatch latch;
    private StartConfig.Callback callback;
    private ThreadPoolExecutor executor;
    private static InetAddress host;
    private int total;
    private long startTime;
    private AtomicInteger currIndex;
    public static int max_thread_count;
    public static int[] IP_LIST = new int[5];
    public static int[] PORT_LIST = new int[2];

    public TcpPortThread(String threadName, int offsetPort, int total, AtomicInteger currIndex, long startTime, CountDownLatch latch,ThreadPoolExecutor executor, StartConfig.Callback callback) {
        super(threadName);
        this.latch = latch;
        this.executor = executor;
        this.callback = callback;
        this.offsetPort = offsetPort;
        this.total = total;
        this.currIndex = currIndex;
        this.startTime = startTime;
    }

    @Override
    public void run() {
        try {
            for (int i = IP_LIST[3]; i <= IP_LIST[4]; i++) {
                StringBuilder ip = new StringBuilder();
                for (int j = 0; j < 3; j++) {
                    ip.append(IP_LIST[j]).append(".");
                }
                ip.append(i);
                try {
                    host = InetAddress.getByAddress(NetUtils.parseIP4Address(ip.toString()));
                } catch (UnknownHostException e) {
                    logger.error("未知主机，ErrorMsg：{}",e.getMessage());
                    e.printStackTrace();
                }
                if(!portScan(host)){
                    return;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            latch.countDown();
        }
    }

    /**
     * 端口扫描
     * @param host
     * @return
     */
    private boolean portScan(InetAddress host) {
        Socket tcpSocket;
        for (int j = PORT_LIST[0] + offsetPort; j <= PORT_LIST[1]; j += max_thread_count) {
            try {
                if(Thread.currentThread().isInterrupted()){
                    Thread.currentThread().interrupt();
                    return false;
                }
                tcpSocket = new Socket();
                SocketAddress socketAddress = new InetSocketAddress(host, j);
                tcpSocket.connect(socketAddress,1000);
                tcpSocket.close();
                if(callback != null){
                    StartConfig.Next next = callback.onFound(host.getHostAddress(), j ,Constant.getServiceName(j, "unknown_service"),this.executor);
                    if (next == StartConfig.Next.STOP_SCAN) {
                        executor.shutdownNow();
                    }
                }
            } catch (SocketException e) {
                logger.debug("{}:{}不可访问，ErrorMsg：{}", host.getCanonicalHostName(), j, e.getMessage());
            } catch (Exception e) {
                logger.debug("{}:{}不可访问，ErrorMsg：{}", host.getCanonicalHostName(), j, e.getMessage());
            } finally {
                if(callback != null){
                    progress(j);
                }
            }
        }
        return true;
    }

    /**
     * 加锁保证打印进度顺序性
     * @param j
     */
    private void progress(int j){
        synchronized (TcpPortThread.class){
            int index = currIndex.incrementAndGet();
            callback.onProgress(host.getHostAddress(), j, total, index, (int)((index / (double)total) * 100), (System.currentTimeMillis() - startTime));
        }
    }
}